﻿#pragma once

#include "windows.h"

#include <stdlib.h>
#include <string>
#include <boost/optional.hpp>
#include <boost/lexical_cast.hpp>


namespace lix{


	//==============================================================================
	//! errno を参照するエラーコードポリシー
	class CRTErrorNoCategory {
	public:
		typedef errno_t code_type;
		static errno_t NoError() { return 0; }
		static std::wstring GetTextW( code_type code);
		static std::string GetText( code_type code);
	};

	//==============================================================================
	//! GetLastError を使用するエラーコードポリシー
	class Win32ErrorCategory{
	public:
		typedef DWORD code_type;
		static code_type NoError() { return 0; }
		static std::wstring GetTextW( code_type code);
		static std::string GetText( code_type code);
	};

	//==============================================================================
	//! 型に対応するエラーコードポリシー
	//! エラーコードの扱いが、エラーコードの型から一意に決まる場合、
	//! このテンプレートを特殊化して使用する。
	
	template <class T> 
	class DefaultErrorCodeTraits{
	public:
		typedef T code_type;
		typedef T code_type;
		static code_type NoError() { return T(); }

		static std::wstring GetTextW( code_type code){
			return boost::lexical_cast<wstring>(code);
		}
		static std::string GetText( code_type code){
			return boost::lexical_cast<string>(code);
		}
	};
	
	template <class T>
	class ErrorCodeTraits : public DefaultErrorCodeTraits<T>{
	};


	//==============================================================================
	//! 失敗時のコンストラクタオーバーロードのための型
	//! @tparam T エラーコードの型
	template<class T>
	class FailedArgType{
	public:
		//! コンストラクタ、エラーコードを受ける。
		FailedArgType( T& t) : m_ErrorCode(t) {}

		//! エラーコード
		T m_ErrorCode;
	};

	//==============================================================================
	// 成功時のコンストラクタオーバーロードのための型
	class SucceededArgType{
	public:
		SucceededArgType(){}
	};

	//==============================================================================
	//! 『失敗しうる値』のベーステンプレート
	//! 
	//! 何らかの値とエラーコードを持つイミュータブルな値型。
	//! 概念的には boost::optional にエラーコードを持たせることができるようにしたもの。
	//! 
	//! @tparam T				正常時の内容の型
	//! @tparam ErrorCategory	エラーコードの扱いを示すポリシー
	//! 
	template< typename T, typename ErrorCodeType, typename ErrorCategory = ErrorCodeTraits<ErrorCodeType> >
	class FailableBase {
	private:
		// safe bool イディオムのため。
		typedef void (FailableBase::*bool_type)() const;
		void safe_bool_dummy_member() const {}

	public:
		typedef FailableBase<T, ErrorCodeType, ErrorCategory> this_type;
		typedef ErrorCodeType error_code_type;
		typedef T value_type;



		//! コンストラクタ。
		//! @param body 値、ここに boost::none を渡すと、失敗したことを示す。
		//! @param code エラーコード
		FailableBase( const boost::optional<T>& body, error_code_type code = ErrorCategory::NoError())
			:m_Body(body), m_ErrorCode(code)
		{}

		// 成功を表すコンストラクタ
		FailableBase( const T& t)
			:m_Body( boost::optional<T>(t)), m_ErrorCode(ErrorCategory::NoError())
		{}

		//! 失敗を表すコンストラクタ
		template<class T>
		FailableBase( const FailedArgType<T>& code) 
			:m_Body( boost::none),m_ErrorCode(code.m_ErrorCode)
		{}

		//! 失敗を表す生成関数。
		static this_type MakeFailed( error_code_type code ){
			return this_type( boost::none, code );
		}

		//! デリファレンス
		//! 値の取得、ポインタ様のアクセスになる。
		//! @throw std::logic_error 値を持っていないとき。
		const T& operator*() const {
			return this->Get();
		}

		//! 値オブジェクトへのアクセス。
		//! @throw std::logic_error 値を持っていないとき。
		const T& operator->() const {
			return m_Body.operator->();
		}

		//! 失敗時(値を持っていないとき) true を返す。
		bool IsFailed() const {
			return (! m_Body);
		}

		//! 値を boost::optional として返す。
		const boost::optional<T>& GetOptional() const {
			return m_Body;
		}

		//! 値の取得。
		//! @throw std::logic_error 値を持っていないとき。
		const T& Get() const {
			if( ! m_Body ){
				throw std::logic_error("attempt to dereference uninitialized value.");
			}
			return *m_Body;
		}

		//! 値の取得、値を持っていない場合は引数で与えた値を返す。
		T GetValueOr(const T& t ) const {
			if( this->IsFailed()){
				return t;
			}
			return *m_Body;
		} 

		//! エラーコード取得
		error_code_type GetErrorCode() const {
			return m_ErrorCode;
		}

		//! bool への変換。
		operator bool_type() const {
			return ( ! this->IsFailed() ) ?  &FailableBase::safe_bool_dummy_member : NULL;
		}

		//! エラーメッセージ
		std::string GetErrorMessage() const {
			return ErrorCategory::GetText( m_ErrorCode );
		}
		//! エラーメッセージ(ワイド版)
		std::wstring GetErrorMessageW() const {
			return ErrorCategory::GetTextW( m_ErrorCode );
		}

		//! 文字列への変換、
		//! 値がある場合はそれを string に lexical_cast したものを、
		//! ない場合は ErrorCategory によって文字列化した結果を返す。
		std::string ToString() const {
			if( this->IsFailed() ){
				return ErrorCategory::GetText( m_ErrorCode );
			}
			return boost::lexical_cast<std::string>( *m_Body );
		}

		//! ワイド文字列への変換、
		//! 値がある場合はそれを wstring に lexical_cast したものを、
		//! ない場合は ErrorCategory によってワイド文字列化した結果を返す。
		std::string ToWString() const {
			if( this->IsFailed() ){
				return ErrorCategory::GetTextW( m_ErrorCode );
			}
			return boost::lexical_cast<std::wstring>( *m_Body );
		}
	private:
		boost::optional<T> m_Body;
		error_code_type m_ErrorCode;
	};

	//! FailableBase の出力
	template < typename T, typename Code, typename Category >
	static std::ostream& operator<<(  std::ostream& os, FailableBase<T, Code, Category>& failable){
		os << failable.ToString();
		return os;
	}

	//! FailableBase の出力(ワイド版)
	template < typename T, typename Code, typename Category >
	static std::wostream& operator<<(  std::wostream& os, FailableBase<T, Code, Category>& failable){
		os << failable.ToWString();
		return os;
	}

	//==============================================================================
	//! Win32 の LastError を使用する FailableBase
	
	// template <typename T>
	// using WinFailable = FailableBase<T, Win32ErrorCategory >;

	template< typename T>
	class WinFailable : public FailableBase<T, DWORD, Win32ErrorCategory> {
	public:
		// using FailableBase::FailableBase;
		WinFailable( const boost::optional<T>& body, error_code_type code = NO_ERROR)
			:FailableBase(body, code)
		{}

		template<class T>
		WinFailable( const FailedArgType<T>& code) 
			:FailableBase( boost::none, code.m_ErrorCode)
		{}

		WinFailable( const T& t)
			:FailableBase(t)
		{}
	};

	
	//==============================================================================
	//! errno を使用する FailableBase

	// template <typename T>
	// using CRTFailable = FailableBase<T, CRTErrorNoCategory >;
	template< typename T>
	class CRTFailable : public FailableBase<T, errno_t, CRTErrorNoCategory> {
	public:
		// using FailableBase::FailableBase;
		CRTFailable( const boost::optional<T>& body, error_code_type code = 0)
			:FailableBase(body, code)
		{}

		template<class T>
		CRTFailable( const FailedArgType<T>& code) 
			:FailableBase( boost::none, code.m_ErrorCode)
		{}

		CRTFailable( const T& t)
			:FailableBase(t)
		{}
	};


	//==============================================================================
	//! 成否とエラーコードを保持する値のベースクラス。
	template < typename ErrorCodeType, typename ErrorCategory = ErrorCodeTraits<ErrorCodeType> >
	class Succession {
	private:
		typedef void (Succession::*bool_type)() const;
		void safe_bool_dummy_member() const {}
	
	public:
		typedef Succession<ErrorCategory> this_type;
		typedef typename ErrorCategory::code_type error_code_type;

		Succession( bool success, error_code_type code = ErrorCategory::NoError() )
			: m_IsSucceeded( success), m_ErrorCode( code )
		{}
		Succession( const SucceededArgType& success, error_code_type code = ErrorCategory::NoError() )
			: m_IsSucceeded( true ), m_ErrorCode( code )
		{}
		template<class T>
		Succession( const FailedArgType<T>& faild )
			: m_IsSucceeded( false ), m_ErrorCode( faild.m_ErrorCode )
		{}

		//! 失敗時 true を返す。
		bool IsFailed() const {
			return (! m_IsSucceeded);
		}
		//! 失敗時 true を返す。
		bool IsSucceeded() const {
			return m_IsSucceeded;
		}

		//! bool への変換。
		operator bool_type() const {
			return ( m_IsSucceeded ) ?  &Succession::safe_bool_dummy_member : NULL;
		}

		//! エラーコード取得
		error_code_type GetErrorCode() const {
			return m_ErrorCode;
		}

		//! エラーメッセージ
		std::string GetErrorMessage() const {
			return ErrorCategory::GetText( m_ErrorCode );
		}
		//! エラーメッセージ(ワイド版)
		std::wstring GetErrorMessageW() const {
			return ErrorCategory::GetTextW( m_ErrorCode );
		}

		//! 文字列への変換、
		//! 成功時は "succeeded" という文字列
		//! 失敗時は ErrorCategory によって文字列化した結果を返す。
		std::string ToString() const {
			if( this->IsFailed() ){
				return ErrorCategory::GetText( m_ErrorCode );
			}
			return "succeeded";
		}

		//! ワイド文字列への変換、
		//! 成功時は L"succeeded" という文字列
		//! 失敗時は ErrorCategory によってワイド文字列化した結果を返す。
		std::string ToWString() const {
			if( this->IsFailed() ){
				return ErrorCategory::GetTextW( m_ErrorCode );
			}
			return L"succeeded";
		}
	private:
		bool m_IsSucceeded;
		error_code_type m_ErrorCode;

	};

	//! SuccessionBase の出力
	template < typename T, typename Category >
	static std::ostream& operator<<(  std::ostream& os, Succession<T, Category>& v){
		os << v.ToString();
		return os;
	}

	//! SuccessionBase の出力(ワイド版)
	template < typename T, typename Category, typename Code >
	static std::wostream& operator<<(  std::wostream& os, Succession<T, Category>& v){
		os << v.ToWString();
		return os;
	}



	//! Win32 の LastError を使用する SuccessionBase
	typedef Succession<DWORD, Win32ErrorCategory> WinSuccession;

	//! errno を使用する SuccessionBase
	typedef Succession<errno_t, CRTErrorNoCategory> CRTSuccession;
	





	template< class T>
	static FailedArgType<T> Failed( T t){
		return FailedArgType<T>(t);
	}

	static FailedArgType<DWORD> FailedWithLastError(){
		return Failed( GetLastError());
	}
	static FailedArgType<errno_t> FailedWithErrorNo(){
		errno_t err;
		_get_errno(&err);
		return Failed( err );
	}

	static SucceededArgType Succeeded(){
		return SucceededArgType();
	}
	
}
